//
// Created by yym on 2019/11/19.
//

#include "binaryzation.h"


unsigned char* otsu(int width,int height,unsigned char *data32,int nThreshold)
{
    unsigned char *data8 = getData8(width, height, data32);
    int N = height*width;
    if(nThreshold<0 || nThreshold>255)
    {
        double nHistogram[256];
        getHistogram(width, height, data8, nHistogram);
        double dVariance[256];
        int i = 0;
        for(i=0; i<256; i++)
        {
            dVariance[i] = 0.0;
        }
        double Pa=0.0;
        double Pb=0.0;
        double Wa=0.0;
        double Wb=0.0;
        double W0=0.0;
        double dData1=0.0,  dData2=0.0;

        for(i=0; i<256; i++)
        {
            nHistogram[i] /= N;
            W0 += i*nHistogram[i];
        }
        for(i=0; i<256; i++)
        {
            Pa += nHistogram[i];
            Pb = 1-Pa;
            dData1 += i*nHistogram[i];
            dData2 = W0-dData1;
            Wa = dData1/Pa;
            Wb = dData2/Pb;
            dVariance[i] = (Pa*Pb* pow((Wb-Wa), 2));
        }

        double temp=0.0;
        for(i=0; i<256; i++)
        {
            if(dVariance[i]>temp)
            {
                temp = dVariance[i];
                nThreshold = i;
            }
        }
    }

    return getData1(width, height, data8, data32, nThreshold);
}


void getHistogram(int width,int height,unsigned char *data8,double *nHistogram)
{
    int N = height*width;
    int i = 0;
    for(i=0; i<256; i++)
    {
        nHistogram[i] = 0;
    }
    for(i=0; i<N; i++)
    {
        unsigned char nData = data8[i];
        nHistogram[nData]++;
    }
}

unsigned char* getData8(int width, int height, unsigned char *data32)
{
    int size = width*height*4;
    unsigned char *data8;
    data8 = (unsigned char *)malloc(size/4);
    int index=0;
    int i = 0;
    for(i=0; i<size; i=i+4)
    {
        unsigned char r = data32[i];
        unsigned char g = data32[i+1];
        unsigned char b = data32[i+2];
        unsigned char gray = r * 0.299 + g * 0.587 + b * 0.114;
        data8[index++] = gray;
        data32[i] = gray;
        data32[i + 1] = gray;
        data32[i + 2] = gray;
    }
    return data8;
}


unsigned char *getData1(int width, int height, unsigned char *data8,  unsigned char *data32,int nThreshold)
{
    int N = width * height;
    unsigned char *data1 = (unsigned char *) malloc(N / 8);
    unsigned char piexBit = 0;
    int index = 0;
    int i = 0;
    int j = 0;
    for (i = 0; i < N; i = i + 8) {
        for (j = 0; j < 8; j++) {
            if (data8[i + j] < nThreshold) {
                piexBit |= (1 << (7 - j));
            } else {
                piexBit &= ~(1 << (7 - j));
            }
        }
        data1[index++] = piexBit;
    }
    for (i = 0; i < N*4; i = i + 4) {
        if (data32[i] < nThreshold) {
            data32[i] = 0;
            data32[i + 1] = 0;
            data32[i + 2] = 0;
        } else{
            data32[i] = 255;
            data32[i + 1] = 255;
            data32[i + 2] = 255;
        }
    }
    free(data8);
    return data1;
}